#pragma once 

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <sstream>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>  //stat 
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <algorithm>
#include <fcntl.h>
#include "Util.hpp"
#include "log.hpp"

#define WWW_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_404 "404.html"
#define SEP ": "

#define OK 200
#define NOT_FOUND 404
#define BAD_REQUEST 400
#define SERVER_ERROR 500

static std::string Code2Desc(int code)  //状态码转换 
{
  std::string desc;
  switch(code)
  {
    case 200:
      desc =  "OK";
      break;
    case 404:
      desc = "NOT FOUND";
      break;
    default:
      break;
  }
  return desc;
}

static std::string  Suffix2Desc(const std::string &suffix)  //后缀转换
{
  static std::unordered_map<std::string, std::string> suffix2desc = {
    {".html", "text/html"},
    {".css", "text/css"},
    {".js", "application/javascript"},
    {".jpg", "application/x-jpg"},
    {".xml", "application/xml"},
  };
  auto iter = suffix2desc.find(suffix);
  if(iter != suffix2desc.end())
  {
    return iter->second;
  }
  return "text/html";
}

//状态码描述
//class Code2Desc
//{
//  private:
//    std::unordered_map<int, std::string> code2desc;
//  public:
//    Code2Desc()
//    {}
//
//    void InitCode2Des()
//    {
//      code2desc.insert();
//    }
//
//    ~Code2Desc(){}
//};

//请求与响应 
class HttpRequest 
{
  public:
    std::string request_line;
    std::vector<std::string> request_header;
    std::string blank;
    std::string request_body;

    //解析后的结果也放这
    std::string method;
    std::string uri;//path?args
    std::string version;

    std::unordered_map<std::string , std::string> header_kv;
    int content_length;
    std::string path;
    std::string suffix; // 后缀
    std::string query_string;

    bool cgi;
    int size;
  public:
    HttpRequest():content_length(0),cgi(false){}
    ~HttpRequest(){}
};


class HttpResponse
{
  public:
    std::string status_line;
    std::vector<std::string> response_header;
    std::string blank;
    std::string response_body;

    int status_code;
    int fd;
    //int size;
  public:
    HttpResponse():blank(LINE_END), status_code(OK), fd(-1){}
    ~HttpResponse(){}
};


//读取请求，分析请求，构建响应
//IO通信
class EndPoint
{
  private:
    int sock;
    HttpRequest http_request;
    HttpResponse http_response;
    //bool stop;
  private:
    void RecvHttpRequestLine()
    {
      //读取请求行
      auto &line = http_request.request_line;
      Util::Readline(sock, line);
      line.resize(line.size() - 1);
      //std::cout << "#################################hello test#######################################" << std::endl;
      LOG(INFO, http_request.request_line);
    }

    void RecvHttpRequestHeader()
    {
      std::string line;
      while(true)
      {
        line.clear();
        Util::Readline(sock, line);
        if(line == "\n")
        {
          http_request.blank = line;
          break;
        }
        line.resize(line.size() - 1);//去掉\n
        http_request.request_header.push_back(line);
        //LOG(INFO, line);
      }
      //if(line == "\n") http_request.blank = line;
    }

    void ParseHttpRequestLine()
    {
      auto &line = http_request.request_line;
      std::stringstream ss(line);
      ss >> http_request.method >> http_request.uri >> http_request.version;

      //LOG(INFO, http_request.method);
      //LOG(INFO, http_request.uri);
      //LOG(INFO, http_request.version);
      
      //大小写转换
      auto &method = http_request.method;
      std::transform(method.begin(), method.end(), method.begin(), ::toupper);
   }

     void  ParseHttpRequestHeader()
     {
       std::string key;
       std::string value;
       for(auto &iter : http_request.request_header)
       {
         if(Util::CutString(iter, key, value, SEP))
         {
           http_request.header_kv.insert({key, value});
         }
         // for debug 
         //std::cout << "DEBUG: " << key << std::endl;
         //std::cout << "DEBUG: " << value << std::endl;
       }
     }

     bool IsNeedRecvHttpRequestBody()
     {
       auto &method = http_request.method;
       if(method == "POST")
       {
         auto &header_kv = http_request.header_kv;
         auto iter = header_kv.find("Content-Length");
         if(iter != header_kv.end())
         {
           http_request.content_length = atoi(iter->second.c_str());//转为整数
           return true;
         }
       }
       return false;
     }

    void RecvHttpRequestBody()
    {
       if(IsNeedRecvHttpRequestBody())
       {
         int content_length = http_request.content_length;
         auto &body = http_request.request_body;

         char ch = 0;
         while(content_length)
         {
           ssize_t s = recv(sock, &ch, 1, 0);
           if(s > 0)
           {
             body.push_back(ch);
             content_length--;
           }
           else 
           {
             break;
           }
         }
       }
     }

    //int ProcessNonCgi(int size)
    int ProcessNonCgi()
    {
      //1) 状态行描述完成 2）响应报头待开始
      http_response.fd = open(http_request.path.c_str(), O_RDONLY);
      if(http_response.fd  >= 0)
      {
        //http_response.status_line = HTTP_VERSION;
        //http_response.status_line += " ";
        //http_response.status_line += std::to_string(http_response.status_code);
        ////状态码描述
        //http_response.status_line += " ";
        //http_response.status_line += Code2Desc(http_response.status_code);
        //http_response.status_line += LINE_END;//添加行结束

        ////http_request.path;
        ////wait to do 
        ////http_response.fd = open(http_request.path.c_str(), O_RDONLY);
        ////http_response.size = size; //此处变到后面

        //std::string header_line = "Content-Type: ";
        //header_line += Suffix2Desc(http_request.suffix);
        //header_line += LINE_END; 
        //http_response.response_header.push_back(header_line);
        //
        ////hander_line  += std::to_string(size); //加正文长度
        //header_line = "Content-Length: ";
        //header_line += std::to_string(size); 
        //header_line += LINE_END; 
        //http_response.response_header.push_back(header_line);

        return OK;
      }
      return NOT_FOUND;
    }

    int ProcessCgi()
    {
      //int code = OK;
      //父进程数据 
      int code = OK;
      auto &method = http_request.method; 
      auto &query_string = http_request.query_string; //GET
      auto &body_text = http_request.request_body;    //POST
      auto &bin = http_request.path; //可执行程序,此文件一定存在
      
      int content_length = http_request.content_length;
      auto &response_body = http_response.response_body;

      std::string query_string_env;
      std::string method_env;
      std::string content_length_env;// POST

      //站在父进程角度理解
      int input[2];
      int output[2];

      if(pipe(input) < 0)
      {
        LOG(ERROR, "pipe input error!");
        code = SERVER_ERROR;
        return code;
      }

      if(pipe(output) < 0)
      {
        LOG(ERROR, "pipe output error!");
        code = SERVER_ERROR;
        return code;
      }
      
      //std::cout << "debug : " << "############Use CGI model######################" << std::endl;
      //新线程，但是从头到尾都只有一个进程，就是httpserver!
      pid_t pid = fork();  //保证正常通信，关闭相应读写段
      if(pid == 0)
      { //child
        close(input[0]);
        close(output[1]);//子进程读，所以关闭1


        method_env = "METHOD=";    //子进程区分是通过方法或者环境变量
        method_env += method; 
        putenv((char*)method_env.c_str());

        if(method == "GET")    //GET 环境变量方法
        {
          //KV结构
          query_string_env = "QUERY_STRING=";
          query_string_env += query_string;
          putenv((char*)query_string_env.c_str());
          LOG(WARNING, "Get method, add query_string env!");
        }
        else if(method == "POST")
        {
          content_length_env = "CONTENT_LENGTH=";
          content_length_env += std::to_string(content_length);
          putenv((char*)content_length_env.c_str());
          LOG(INFO, "POST method , add Content_length_ENV");
        }
        else 
        {
          //Nothing to do ，保证代码完整
        }
        std::cout << "bin: " << bin << std::endl; 
        //站在子进程角度
        //input[1]: 写出， 往1，  实际往input[1]写
        //output[0]:读入   往0 ， 实际output[0]写
        dup2(output[0], 0);
        dup2(input[1], 1);
        
        //exec* 程序替换
        execl(bin.c_str(), bin.c_str(), nullptr);
        exit(1);
      }
      else if(pid < 0) 
      { //error
        LOG(ERROR, "fork error!");
        return 404;
      }
      else 
      { //parent
        //wait 线程等待
        close(input[1]);//父进程关闭 写
        close(output[0]);//父进程关闭 读
        
        if(method ==  "POST")    //POST   方法
        {
          //int size = body_text.size();
          const char * start = body_text.c_str();
          int total = 0;
          int  size = 0;
          while(total < content_length &&  (size = write(output[1], start + total, body_text.size()-total)) > 0)
          {
            //size = write(output[1], start + total, body_text.size()-total);
              total += size;
          }
        }

        // 拿到结果  父进程
        char ch = 0;
        while(read(input[0], &ch, 1) > 0)
        {
          response_body.push_back(ch);
        }

        int status = 0;
        pid_t ret =  waitpid(pid, &status, 0);
        if(ret == pid)
        {
          if(WIFEXITED(status))
          {
            if(WEXITSTATUS(status) == 0)
            {
              code = OK;
            }
            else 
            {
              code = BAD_REQUEST;
            }
          }
          else 
          {
            code = SERVER_ERROR;
          }
        }
        close(input[0]);
        close(output[1]);
      }
      return code;
    }
    
    void HandlerError(std::string page) //返回404页面
    {
      std::cout << "debug: " << page << std::endl;
      http_request.cgi = false;
      //正文，一定要给用户返回对应404页面
      http_response.fd = open(page.c_str(), O_RDONLY);
      if(http_response.fd > 0)
      {
        struct stat st;
        stat(page.c_str(), &st);
        http_request.size = st.st_size; 

        std::string line = "Content-Type: text/html";
        line += LINE_END; 
        http_response.response_header.push_back(line);

        line = "Content-Length: ";
        line += std::to_string(st.st_size);
        line += LINE_END;
        http_response.response_header.push_back(line); // 插入响应报头
      }
    }

    void BuildOkResponse()
    {
      // 
      std::string line = "Content-Type: ";
      line += Suffix2Desc(http_request.suffix);
      line += LINE_END; 
      http_response.response_header.push_back(line);
      
      line = "Content-Length: ";
      if(http_request.cgi)
      {
        line += std::to_string(http_response.response_body.size());//POST
      }
      else 
      {
        line += std::to_string(http_request.size); //GET
      }
      line += LINE_END;
      http_response.response_header.push_back(line); // 插入响应报头
    }

    void  BuildHttpReponseHelper()
    {
      //http_request;
      //http_response;
      auto &code = http_response.status_code;

      //1) 构建状态行 
      auto &status_line = http_response.status_line;
      status_line += HTTP_VERSION;
      status_line += " ";
      status_line += std::to_string(code);
      status_line += " ";
      status_line += Code2Desc(code);
      status_line += LINE_END;

      //2)构建响应正文，可能包括响应报头
      std::string path = WWW_ROOT;
      path += "/"; 
      switch(code)
      {
        case OK:
          BuildOkResponse();
          break;
        case NOT_FOUND: 
          path += PAGE_404;
          HandlerError(path); //返回404页面
          break;
        case BAD_REQUEST:
          path += PAGE_404;
          break;
        case SERVER_ERROR:
          path += PAGE_404;
          break;
        //case 500:
        //  HandlerError(PAGE_500);// 
        //  break;
        default:
          break;
      }
    }

//################################## EndPoint  Public ########################################################    
  public:
    EndPoint(int _sock):sock(_sock)//,stop(false)
    {}

//    bool IsStop()
//    {
//      return stop;
//    }

    void RecvHttpRequest()
    {
      //std::cout << "#################################hello RecvHttpReques test#######################################" << std::endl;
      RecvHttpRequestLine();
      RecvHttpRequestHeader();
      //解析HttpRequest 
      ParseHttpRequestLine();
      ParseHttpRequestHeader();
      RecvHttpRequestBody();
    }

    void BuildHttpReponse()
    {
      std::string _path;
      struct stat st;
      //int size = 0;
      std::size_t found = 0;
      auto &code = http_response.status_code;
      if(http_request.method != "GET" && http_request.method != "POST")
      {
        //非法请求
        LOG(WARNING, "method is not right");
        code = BAD_REQUEST;
        goto END;
      }
      //正确请求
      if(http_request.method == "GET")
      {
        size_t pos = http_request.uri.find('?');
        if(pos != std::string::npos)
        {
          Util::CutString(http_request.uri, http_request.path, http_request.query_string, "?");
          http_request.cgi = true;
        }
        else 
        {
          http_request.path = http_request.uri;
        }
      }
      //std::cout<<"debug: uri: " << http_request.uri << std::endl;
      //std::cout<<"debug: path: " << http_request.path << std::endl;
      //std::cout<<"debug: query_string: " << http_request.query_string << std::endl;

      else if(http_request.method == "POST")
      {
        //POST方法
        http_request.cgi = true;
        http_request.path = http_request.uri; 
      }
      else 
      {
        //Nothing to do 
      }

      _path = http_request.path;
      http_request.path = WWW_ROOT;
      http_request.path += _path;

      //std::cout << "debug: " << http_request.path << std::endl;

      if(http_request.path[http_request.path.size()-1] == '/') //请求跟目录
      {
        http_request.path += HOME_PAGE;//访问首页
      }

     //std::cout << "debug: " << http_request.path << std::endl;

      if(stat(http_request.path.c_str(), &st) == 0)
      {
        //资源存在
        //是目录？或其他index.html
        if(S_ISDIR(st.st_mode))
        {
          //说明请求的资源是一个目录，不被允许，需要做相关处理
          //虽然是一个目录，但是绝对不会以斜杠结尾
          http_request.path += "/";
          http_request.path += HOME_PAGE;
          stat(http_request.path.c_str(), &st);// **** 更新状态，如果是目录更新，不是目录不做处理
        }
        //请求的是一个可执行程序？需要特殊处理的！
        if((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
        {
          //说明是可执行文件，需要特殊处理
          http_request.cgi = true; 
        }
        //size = st.st_size;  // 获取size大小
        http_request.size = st.st_size; 
      }
      else 
      {
        //资源不存在
        //std::string info = http_request.path;
        //info += " Not Found!";
        LOG(WARNING, http_request.path + " Not Found");
        code = NOT_FOUND;
        goto END;
      }

      //走到此处，表明：path 找后缀等
      found = http_request.path.rfind(".");
      if(found == std::string::npos)
      {
        http_request.suffix = ".html";
      }
      else 
      {
        http_request.suffix = http_request.path.substr(found);
      }

      //走到此处，表明正确，走了CGI
      if(http_request.cgi)
      {
        code = ProcessCgi(); //执行目标程序，拿到结果到http_response.response_body;
      }
      else 
      {
        //1.  目标网页一定是存在的
        //2.  返回并不是单单的返回网页，而是要构建HTTP响应
        // size 是 response的参数
        code = ProcessNonCgi(); //简单的网页返回 ,返回静态网页,只需要打开网页即可 
      }

END:
      BuildHttpReponseHelper();//状态行填充了，响应报头也有了，空行也有了，正文也有了
      //if(code != OK)
      //{
      //错误码处理 
      //  BuildHttpReponseHelper(code);//状态行填充了，响应报头也有了，空行也有了，正文也有了
      //}
      //return;
    }

    void SendHttpResponse()
    {
      send(sock, http_response.status_line.c_str(), http_response.status_line.size(), 0);
      for(auto iter : http_response.response_header)
      {
        send(sock, iter.c_str(), iter.size(), 0);
      }
      send(sock, http_response.blank.c_str(), http_response.blank.size(), 0);

      //正文发送
      if(http_request.cgi)
      {
        //CGI
        auto &response_body = http_response.response_body;
        size_t size = 0;
        size_t total = 0;
        const char *start = response_body.c_str();
        while(total < response_body.size() && (size = send(sock, start + total, response_body.size() - total, 0)) > 0)
        {
          total += size; 
        }
      }
      else 
      {
        sendfile(sock, http_response.fd, nullptr, http_request.size);
        close(http_response.fd);
      }
    }

    ~EndPoint()
    {
      close(sock);
    }
};


//#define DEBUG 1
class Entance
{
  public:
    static void  *HandlerRequest(void *_sock)
    {
      LOG(INFO, "Handler Request Begin");
      int sock = *(int*)_sock;
      delete (int*)_sock;
//      std::cout << "get a new link ... : " << sock << std::endl;
//
#ifdef DEBUG  
      //For Test
      char buffer[4096];
      recv(sock, buffer, sizeof(buffer), 0);
      std::cout << "------------------------------begin---------------------------"<<std::endl;
      std::cout << buffer << std::endl;
      std::cout << "------------------------------end---------------------------"<<std::endl;
      //std::cout << "#################################hello ELSE test#######################################" << std::endl;
#else 
      EndPoint *ep = new EndPoint(sock);
      ep->RecvHttpRequest();
      ep->BuildHttpReponse();
      ep->SendHttpResponse();
      delete ep;
#endif 
     LOG(INFO, "heandler Request End"); 
     // std::string line;
     // Util::Readline(sock, line);

     // std::cout<<line<<std::endl;
     // 
     // close(sock);
     return nullptr;
    }
};

//class CallBack
//{
//  public:
//    CallBack()
//    {}
//
//    void operator()(int sock)
//    {
//      HandlerRequest(sock);
//    }
//
//    void HandlerRequest(int sock)
//    {
//      LOG(INFO, "Handler Request Begin");
//#ifdef DEBUG 
//      char buffer[4096];
//      recv(sock, buffer, sizeof(buffer), 0);
//      std::cout << "------------------------------begin---------------------------"<<std::endl;
//      std::cout << buffer << std::endl;
//      std::cout << "------------------------------end---------------------------"<<std::endl;
//#else 
//      EndPoint *ep = new EndPoint(sock);
//      ep->RecvHttpRequest();
//      if(!ep->IsStop())
//      {
//        LOG(INFO, "Recv No error, begin Build And send");
//        ep->BulidHttpReponse();
//        ep->SendHttpResponse();
//      }
//      else 
//      {
//        LOG(WARNING, "Recv Error, Stop Build And Send");
//      }
//      delete ep;
//#endif 
//      LOG(INFO, "Hander Request End");
//
//    }
//    ~CallBack()
//    {}
//};
